package com.ljh.trans.controller;

import com.ljh.trans.service.LogService;
import com.ljh.trans.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * @author: Themberfue
 * @date: 2025/3/27 19:50
 * @description:
 */
@Slf4j
@RequestMapping("/user2")
@RestController
public class UserController2 {
    @Autowired
    private UserService userService;

    @Autowired
    private LogService logService;

    // TODO 声明式事务
    // ! 通过注解自动开始、提交或回滚事务
    // ? 程序正常执行时，事务自动提交。
    // ? 程序发生异常时，若捕获到异常但未抛出(throw)则继续自动提交，若捕获到异常且抛出则进行事务回滚
    @Transactional
    @RequestMapping("/registry")
    public String registry(String username,String password) throws Exception {
        // 用户注册
        Integer result = userService.registryUser(username,password);
        log.info("用户注册成功, 影响行数:" + result);

        try {
            int a = 10 / 0;
        } catch (Exception e) {
            log.error("发生异常");
            throw new RuntimeException(e); // ! 需要注意的是，并非抛出任何异常后，都会进行事务回滚
        }
        return "注册成功";
    }

    @Transactional
    @RequestMapping("/r2")
    public String r2(String username,String password) throws Exception {
        // 用户注册
        Integer result = userService.registryUser(username,password);
        log.info("用户注册成功, 影响行数:" + result);

        try {
            int a = 10 / 0;
        } catch (Exception e) {
            log.error("发生异常");
            // ? 手动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return "注册成功";
    }

    // ! 指定异常范围
    // * @Transactional(rollbackFor = Exception.class)
    @Transactional(rollbackFor = {IOException.class, RuntimeException.class, Error.class})
    @RequestMapping("/r3")
    public String r3(String username,String password) throws Exception {
        // 用户注册
        Integer result = userService.registryUser(username,password);
        log.info("用户注册成功, 影响行数:" + result);

        try {
            int a = 10 / 0;
        } catch (Exception e) {
            log.error("发生异常");
            throw new Exception(e); // ! 通过指定异常范围后，该范围内异常被抛出后都会进行事务回滚
        }
        return "注册成功";
    }

    // ! 指定事务的隔离级别
    // ? DEFAULT：以连接的数据库的事务隔离级别为准
    // ? READ_UNCOMMITED(读未提交，脏读)：线程B 可以读取 线程A 执行事务时但未提交时的状态
    // ? READ_COMMITED(读已提交，不可重复读)：线程B 可以读取 线程A 执行事务时已提交后的状态
    // ? REPEATABLE_READ(可重复读)(MySQL的默认隔离级别)：线程B 可以读取 线程A 执行事务时但未提交时的状态，但线程B 插入时，会报错
    // ? SERIALIZABLE(序列化)：将线程B 和 线程A 改为串行执行
    @Transactional(isolation = Isolation.READ_COMMITTED)
    @RequestMapping("/r4")
    public String r4(String username,String password) throws Exception {
        // 用户注册
        Integer result = userService.registryUser(username,password);
        log.info("用户注册成功, 影响行数:" + result);

        try {
            int a = 10 / 0;
        } catch (Exception e) {
            log.error("发生异常");
            throw new Exception(e);
        }
        return "注册成功";
    }

    // ! 指定事务的传播机制(多个开启事务的方法存在调用关系时，事务是如何在这些方法间进行传播的)
    // * 存在 A方法 和 B方法，其中 A 调用 B
    // ? REQUIRED：默认的事务传播级别。如果 A 开启事务，则 B 不需额外开启事务；如果 A 未开启事务，则 B 单独开启新的事务
    // ? SUPPORTS：如果 A 开启事务，则 B 不需额外开启事务；如果 A 未开启事务，B 以非事务的方式继续运行
    // ? MANDATORY：强制性，如果 A 开启事务，则 B 不需额外开启事务；如果 A 未开启事务，则 B 抛出异常
    // ? REQUIRES_NEW：无论如何，B 都会单独开启新的事务，且只执行自己开启的事务，事务与事务之间相互独立，互不干扰
    // ? NOT_SUPPORTED：无论如何，B 都以非事务的方式继续运行
    // ? NEVER：无论如何，B 都以非事务的方式继续运行，若 A 开启事务，则 B 抛出异常
    // ? NESTED：如果 A 开启事务，则 B 单独开启事务作为 A 的 "嵌套"事务(A与B建立了联系) 来运行；如果 A 未开启事务，则与 REQUIRED 一致
    // ! 使用 REQUIRED 会使全部事务都回滚，NESTED 可以实现局部事务回滚
    @Transactional(propagation = Propagation.REQUIRED)
    @RequestMapping("/r5")
    public String r5(String username,String password) throws Exception {
        // 用户注册
        Integer result = userService.registryUser(username, password);
        log.info("用户注册成功, 影响行数:" + result);

        Integer logResult = logService.insertLog(username, "用户注册");
        log.info("日志记录成功, 影响行数:" + logResult);

        return "注册成功";
    }
}
